/*
  This is a test server for messaging, that will eventually be
  adapted for use in signalling the HD recorders at the UTas
  VLBI observatories.

*/

#include "socket_test_server.h"

void Die(char *mess, int sock) { perror(mess); close(sock); exit(1); }
void PrintStatus(char *mess) { char outmess[BUFFSIZE]; sprintf(outmess,"%s\n",mess); fprintf(stdout,outmess); }

/* stupid global pointer variable for the child_handler routine */
recorder *recorder_pointer=NULL;
int child_pid=-1; /* stupid signal handlers */

/*
  The HandleClient routine gets called when a connection is made
  to the socket.
*/
void HandleClient(int sock,recorder *settings) {
  char buffer[BUFFSIZE],tag[BUFFSIZE],arg[BUFFSIZE],endtag[BUFFSIZE],failmsg[BUFFSIZE];
  int received = -1,taglen=0,recvlen=0,parse_ret=0,returncode=-1;
  buffer[0]='\0';
  tag[0]='\0';
  arg[0]='\0';
  endtag[0]='\0';
  failmsg[0]='\0';

  /* Receive instruction from client */
  if ((received=recv(sock,buffer,BUFFSIZE,0))<0){
    PrintStatus("Failed to receive initial bytes from client");
    return;
  }
  /*     Die("Failed to receive initial bytes from client",sock);*/

  recvlen=strlen(buffer);

  /* parse the instruction */
  /* the instruction must have the form: <tag>instruction</tag> */
  /* possible tags are: */
  /*   mesg: send a message to the server: instruction is echoed to the screen */
  /*   data: send some data to the server: instruction should be variable=value */
  /*   cmnd: send a command to the server: instruction should be the command name */

  /*  fprint(stdout,"%s\n",buffer); */
  if ((parse_ret=sscanf(buffer,"<%4s>%[^<]</%4s>",tag,arg,endtag))!=3){
    strcpy(failmsg,MALFORMED_REQUEST_ERR);
    returncode=MALFORMED_REQUEST;
    /*    Die("Malformed request!\n",sock); */
  } else if (strcmp(tag,endtag)!=0){
    strcpy(failmsg,MALFORMED_REQUEST_ERR);
    returncode=MALFORMED_REQUEST;
    /*     Die("Mismatch in starting and ending tags!",sock); */
  } else if (strcmp(tag,"mesg")==0){
    returncode=message_handler(arg,failmsg);
  } else if (strcmp(tag,"data")==0){
    PrintStatus("Receiving data from client:");
    PrintStatus(arg);
    returncode=data_handler(arg,failmsg,settings);
  } else if (strcmp(tag,"cmnd")==0){
    PrintStatus("Receiving command from client:");
    PrintStatus(arg);
    returncode=command_handler(arg,failmsg,settings);
    if ((returncode==FILE_UNREADABLE)||(returncode==FILE_UNWRITABLE)){
      /* if the recorder settings are unavailable then we need to exit and alert the user */
      Die(failmsg,sock);
    }
      
  }

  /* send confirmation */
  send_confirmation(returncode,failmsg,sock);
  close(sock);
    
}

/*
  This routine handles the starting and stopping of the recording,
  and the data checking routines. It will check that the recorder
  structure has all the necessary information before doing anything.
*/
int command_handler(char *data,char *failure,recorder *settings){

  /* the valid commands: */
  /*   start-record  = start recorder */
  /*   stop-record   = stop recorder */
  /*   check-latest  = check the latest file */
  /*   check-all     = check all files */
  /*   status-record = get the recorder status */
  /*   status-check  = get the checker status */
  /* some of these commands accept arguments, specified in the form: */
  /*   command:arg1,arg2,...,argN */
  /* note that there are a maximum of 4 accepted arguments */

  /* arguments for check-latest and check-all: */
  /*   fail      = stop testing on first problem */
  /*   pps       = check for the pulse-per-second signal */
  /*   sampler   = check in sampler stats mode */
  /*   countdown = check in countdown mode */
  /*   verbose   = more output from the checker */
  /* the arguments pps, sampler and countdown cannot be specified together, */
  /* the options fail and verbose can be used with all vsib_checker modes */
  /* argument specific to check-latest: */
  /*   n(number) = check the (number) latest files */
  /* if this argument is not specified, we assume n1 */

  /* there are no arguments available for start-record, stop-record, status-record and status-check */
  /* any arguments specified with these commands will be ignored, and an */
  /* error generated. the command will however still be executed. */

  char command[BUFFSIZE],args[MAXCHECKARGS][BUFFSIZE],*tokptr,vargs[MAXCHECKALLOW][BUFFSIZE];
  int nargs,nvargs,i,rec_val=-1,returncode=-1,failflag=-1,verbflag=-1,temp=-1,lastnum=-1;
  
  /* set the valid arguments */
  strcpy(vargs[0],"fail");
  strcpy(vargs[1],"pps");
  strcpy(vargs[2],"sampler");
  strcpy(vargs[3],"countdown");
  strcpy(vargs[4],"verbose");
  nvargs=MAXCHECKALLOW; /* the number of valid arguments */

  /* parse the command string */
  if((tokptr=strtok(data,":"))!=NULL){ /* is there a command? */
    strcpy(command,tokptr);

    nargs=0;
    failflag=0; verbflag=0;
    /* now run through the arguments */
    for (i=0;i<MAXCHECKARGS;i++)
      args[i][0]='\0'; /* initialise the argument array */

    while((tokptr=strtok(NULL,","))!=NULL){
      /* check that it's a valid argument */
      for (i=0;i<nvargs;i++){
	if (strcmp(tokptr,vargs[i])==0){ /* only assign the argument if it's valid */
	  if (nargs<MAXCHECKARGS) /* prevent overflow of the allocated memory */
	    strcpy(args[nargs],tokptr);
	  nargs++; /* but still increment nargs for error checking */
	}
	if (i==0)
	  failflag=1; /* this will help us check if two exclusive arguments were given */
	if (i==4)
	  verbflag=1;
      }
      if (sscanf(tokptr,"n%d",&temp)==1) /* have they specified the number of files to check? */
	lastnum=temp;
      if (nargs>MAXCHECKARGS){
	strcpy(failure,TOO_MANY_ARGS_ERR);
	return(TOO_MANY_ARGS);
      } else if ((nargs==(MAXCHECKARGS-1))&&(failflag==0)&&(verbflag==0)){ /* two arguments, but no fail or verbose arguments */
	strcpy(failure,BAD_CHECK_ARGS_ERR);
	return(BAD_CHECK_ARGS);
      } else if ((nargs==MAXCHECKARGS)&&((failflag==0)||(verbflag==0))){ /* three arguments, but fail and verbose have not both been set */
	strcpy(failure,BAD_CHECK_ARGS_ERR);
	return(BAD_CHECK_ARGS);
      }
    }

    if (lastnum==-1) /* set the number of files to check if it wasn't specified by the user */
      lastnum=1;

    /* which command? */
    if (strcmp(command,"start-record")==0){

      rec_val=recorder_control(RECSTART,settings,failure);
      
    } else if (strcmp(command,"stop-record")==0){

      rec_val=recorder_control(RECSTOP,settings,failure);

    } else if (strcmp(command,"check-latest")==0){

      rec_val=check_control((CHECKLATEST-lastnum),settings,failure,args);

    } else if (strcmp(command,"check-all")==0){

      rec_val=check_control(CHECKALL,settings,failure,args);

    } else if (strcmp(command,"status-record")==0){

      rec_val=status_control(STATUS_RECORD,settings,failure);

    } else if (strcmp(command,"status-check")==0){
      
      rec_val=status_control(STATUS_CHECK,settings,failure);

    } else { /* unrecognised command */
      
      strcpy(failure,UNKNOWN_COMMAND_ERR);
      rec_val=UNKNOWN_COMMAND;

    }
    /* return the return value */
    return(rec_val);

  } else { /* no good, there's nothing there */
    returncode=UNKNOWN_COMMAND;
    strcpy(failure,UNKNOWN_COMMAND_ERR);
    return(returncode);
  }
}

/*
  This routine is responsible for controlling the status checking
  routines.
*/
int status_control(int mode,recorder *settings,char *failure){
  int cerr=-1;

  /* what status has the user requested */
  if (mode==STATUS_RECORD){ /* recorder status */
    /* update the recorder status string so the amount of available recording */
    /* time is accurate */
    if ((cerr=UpdateRecorderStatus(settings,failure))!=NO_ERROR) /* couldn't update the status */
      return(cerr);

    /* the status was updated successfully */
    /* we copy the status string into the failure message buffer */
    strcpy(failure,settings->status_recorder);
    /* we return with an error code, but this is actually a good thing here */
    /* this is done so the message handler replies to the client with the status message */
    return(STATUS_COMPLETE);

  } else if (mode==STATUS_CHECK){ /* checker status */

    /* it is the responsibility of other routines to ensure the checker status */
    /* is updated as needed */
    /* we copy the status string into the failure message buffer */
    strcpy(failure,settings->status_checker);
    /* we return with an error code, but this is actually a good thing here */
    /* this is done so the message handler replies to the client with the status message */
    return(STATUS_COMPLETE);
    

  } else { /* unrecognised request */
    strcpy(failure,STATUS_INVALID_ERR);
    return(STATUS_INVALID);
  }
}

/*
  This routine is responsible for controlling the data
  verification programs.
*/
int check_control(int mode,recorder *settings,char *failure,char check_args[2][BUFFSIZE]){
  FILE *recorder_settings=NULL,*lsresult=NULL;
  int is_recording=-1,nargs,cargs,nchannels,i,lerr,ncheck,dir_check;
  char test_command[BUFFSIZE],**args,commandstring[BUFFSIZE],lscommand[BUFFSIZE],lsbuf[BUFFSIZE];
  int check_pid;

  /* first thing to do is open the file */
  /* /tmp/recorder_settings */
  /* to get the recorder settings, if the recorder is running */
  if ((recorder_settings=fopen("/tmp/recorder_settings","r"))!=NULL){
    fclose(recorder_settings); /* don't need to read, we'll call LoadRecorderSettings to do that */
    if((lerr=LoadRecorderSettings(settings,failure))!=NO_ERROR){ /* we do this again to make sure that the settings weren't changed since start-up */
      return(lerr);
    }
    is_recording=1; /* currently recording */
  } else {
    is_recording=0; /* not currently recording */
  }

  /* check that we have enough information */
  /* this should only be necessary if we're not currently recording, */
  /* but to be safe we'll check even if we are recording */
  if (strlen(settings->directory_name)==0){ /* the directory name hasn't been set */
    strcpy(failure,DIRECTORY_NOT_SET_ERR);
    return(DIRECTORY_NOT_SET);
  }

  if (strlen(settings->filename_prefix)==0){ /* the filename prefix hasn't been set */
    strcpy(failure,FILENAME_NOT_SET_ERR);
    return(FILENAME_NOT_SET);
  }

  /* are we in the correct directory */
  if (settings->in_dir==0){ /* no, we need to change dir */
    if ((dir_check=chdir(settings->directory_name))!=0){ /* try to change into the output directory */
      strcpy(failure,CHDIR_FAILED_ERR);
      return(CHDIR_FAILED);
    } else {
      settings->in_dir=1;
    }
  }

  /* set up the test command */
  commandstring[0]='\0'; /* command string will contain the command to execute */
  strcpy(commandstring,"/home/vlbi/bin/vsib_checker"); /* the command name */
  
  /* now the arguments */
  for (i=0;i<MAXCHECKARGS;i++){
    if (strlen(check_args[i])>0){
      if (strcmp(check_args[i],"sampler")==0){
	sprintf(commandstring,"%s -n 10 -s",commandstring); /* the -n 10 tells the checker to look at only the first 2MB of each file */
      } else if (strcmp(check_args[i],"countdown")==0){
	sprintf(commandstring,"%s -x",commandstring);
      } else if (strcmp(check_args[i],"fail")==0){
	sprintf(commandstring,"%s -f",commandstring);
      } else if (strcmp(check_args[i],"verbose")==0){
	sprintf(commandstring,"%s -v",commandstring);
      } else if (strcmp(check_args[i],"pps")==0){
	sprintf(commandstring,"%s -p",commandstring);
      }
    }
  }

  /* now the list of files to check */
  if (mode==CHECKALL){ /* simple, check everything */
    sprintf(commandstring,"%s %s*",commandstring,settings->filename_prefix);
  } else if (mode<CHECKLATEST){
    /* we need to check some number of the latest files */
    /* the number we need to check is CHECKLATEST-mode */
    ncheck=CHECKLATEST-mode; /* positive number */
    /* now for another dirty hack, from the people who brought you "Beer-Milkshakes" */
    /* we'll use ls to do the date sorting for us... so dirty. */
    sprintf(lscommand,"ls -t %s* > /tmp/ls_result",settings->filename_prefix); /* this will return the files in increasing age */
    system(lscommand); /* do the dirty work */
    if ((lsresult=fopen("/tmp/ls_result","r"))==NULL){ /* can't access /tmp probably */
      strcpy(failure,LATEST_FAIL_ERR);
      return(LATEST_FAIL);
    }
    i=0;
    while(((fgets(lsbuf,BUFFSIZE,lsresult))!=NULL)&&(i<ncheck)){
      /* keep going as long as there is more files, and we haven't checked enough files */
      i++; /* one more file */
      lsbuf[strlen(lsbuf)-1]='\0'; /* get rid of the newline character */
      sprintf(commandstring,"%s %s",commandstring,lsbuf); /* add the name of the file to the check command */
    }
    fclose(lsresult);
    remove("/tmp/ls_result"); /* erase the dirt */
  }

  /* the last thing we need to put on the command line */
  /* is the redirection of the output to file */
  /* we want both stdout and stderr to go to the file, so use redirect >& */
  sprintf(commandstring,"%s >& /tmp/checker_output",commandstring);
  PrintStatus(commandstring);

  /* we're ready to execute the command */
  /* we'll make the code fork here: */
  /* the parent fork will return immediately saying the check has started */
  /* the child fork will run the check command and wait for it to finish */
  /* the SIGCHLD signal from the child will cause the parent to update the checker status */
  if ((check_pid=fork())<0){ /* couldn't fork for some reason */
    strcpy(settings->status_checker,"Unable to run checker."); /* update the checker status */
    strcpy(failure,CHECK_FORK_FAIL_ERR);
    return(CHECK_FORK_FAIL);
  } else if (check_pid==0){ /* are we the child process */
    system(commandstring); /* run the check command and wait for it to finish */
    exit(0); /* tell the parent we're done */
  } else {
    /* tell the user (if they check the status) that the checker is running. */
    strcpy(settings->status_checker,"Checker running.");
    /* and give them the command that is running */
    sprintf(settings->status_checker,"%s\n%s",settings->status_checker,commandstring);
    /* set up a signal handler for the child finishing */
    (void)signal(SIGCHLD,child_handler);
    /* we can't pass the settings array to the child, so we'll store a pointer to */
    /* it in the global variable recorder_pointer */
    recorder_pointer=settings;
    /* and the child's PID */
    child_pid=check_pid;
  }
  
  /* only the parent will get here, since the child has killed itself */
  /* programming is so morbid */
  return(NO_ERROR);
  
}

/*
  This routine is responsible for controlling the hard disk
  recorder.
*/
int recorder_control(int command,recorder *settings,char *failure){

  FILE *recorder_settings=NULL;
  int is_recording=-1,ret_res=-1,dir_check=-1,recorder_pid=-1,num_args=0,i,copy_args=1,lerr;
  char rec_command[BUFFSIZE],sys_command[BUFFSIZE],**recorder_args=NULL,temp_status[BUFFSIZE];
  struct stat check_dir;

  /* first thing to do is open the file */
  /* /tmp/recorder_settings */
  /* to check if the recorder is already running */
  if ((recorder_settings=fopen("/tmp/recorder_settings","r"))!=NULL){
    fclose(recorder_settings); /* don't need to read, we'll call LoadRecorderSettings to do that */
    if((lerr=LoadRecorderSettings(settings,failure))!=NO_ERROR){ /* we do this again to make sure that the settings weren't changed since start-up */
      return(lerr);
    }
    if (settings->process_id!=-1){
      is_recording=1; /* currently recording */
    } else {
      is_recording=0; /* not currently recording */
    }
  } else {
    is_recording=0; /* not currently recording */
  }

  if (command==RECSTART){
    if (is_recording==1){
      strcpy(failure,ALREADY_RECORDING_ERR);
      return(ALREADY_RECORDING);
    } else if (is_recording==0){
      
      /* check that we have all the required information */
      if (strlen(settings->record_time)==0){ /* the recording time hasn't been set */
	strcpy(failure,RECORDING_TIME_ERR);
	return(RECORDING_TIME);
      }

      if (strlen(settings->directory_name)==0){ /* the directory name hasn't been set */
	strcpy(failure,DIRECTORY_NOT_SET_ERR);
	return(DIRECTORY_NOT_SET);
      }

      if (strlen(settings->filename_prefix)==0){ /* the filename prefix hasn't been set */
	strcpy(failure,FILENAME_NOT_SET_ERR);
	return(FILENAME_NOT_SET);
      }

      /* write out the recording status file */
      if ((ret_res=WriteRecorderSettings(settings,failure))!=NO_ERROR)
	return(ret_res);

      /* make the requested directory */
      if ((dir_check=stat(settings->directory_name,&check_dir))==0){ /* something with the directory name already exists */
	if (!S_ISDIR(check_dir.st_mode)){ /* the name of the directory exists as some other type of file */
	  strcpy(failure,DIRECTORY_INVALID_ERR);
	  return(DIRECTORY_INVALID);
	}
	/* otherwise the directory already exists, and we don't need to do anything */
      } else { /* we should make the directory */
	PrintStatus("making output directory");
	sprintf(sys_command,"mkdir %s",settings->directory_name);
	system(sys_command);
      }
      if (!((dir_check=stat(settings->directory_name,&check_dir))==0)&&
	  (S_ISDIR(check_dir.st_mode))){ /* make sure the directory was made correctly */
	strcpy(failure,MKDIR_FAILED_ERR);
	return(MKDIR_FAILED);
      }
      if (settings->in_dir==0){ /* haven't yet changed directory */
	if ((dir_check=chdir(settings->directory_name))!=0){ /* try to change into the output directory */
	  strcpy(failure,CHDIR_FAILED_ERR);
	  return(CHDIR_FAILED);
	} else {
	  settings->in_dir=1;
	}
      }

      /* prepare recording command and arguments */
      if ((recorder_args=malloc(sizeof(char *)))==NULL){
	fprintf(stderr,"malloc failed!\n"); exit(-1);
      }
      strcpy(rec_command,"/home/vlbi/bin/vsib_record");
      if ((recorder_args[0]=malloc(BUFFSIZE*sizeof(char)))==NULL){
	fprintf(stderr,"malloc failed!\n"); exit(-1);
      }
      strcpy(recorder_args[0],rec_command);
      /* make an array of the arguments */
      num_args=2; /* recording time and output filename are always given */
      /* determine the total number of required arguments */
      if (strcmp(settings->compression,"xxxx")!=0)
	num_args++;
      if (strlen(settings->record_start_time)>0)
	num_args++;
      if (settings->vsib_mode!=3)
	num_args++;
      if (settings->bandwidth!=16)
	num_args++;
      /* each argument is actually 2 arguments */
      num_args*=2;
      /* need a NULL argument at the end */
      num_args++;
      /* allocate the space */
      if ((recorder_args=realloc(recorder_args,(num_args+1)*sizeof(char *)))==NULL){
	fprintf(stderr,"realloc failed!\n"); exit(-1);
      }
      for (i=1;i<num_args;i++){
	if ((recorder_args[i]=malloc(BUFFSIZE*sizeof(char)))==NULL){
	  fprintf(stderr,"malloc failed!\n"); exit(-1);
	}
      }
      recorder_args[num_args]=NULL; /* put the NULL argument on the end of the list */
      /* specify the arguments */
      copy_args=1;
      strcpy(recorder_args[copy_args],"-t"); copy_args++;
      sprintf(recorder_args[copy_args],"%s",settings->record_time); /* recording time */
      copy_args++;
      strcpy(recorder_args[copy_args],"-o"); copy_args++;
      sprintf(recorder_args[copy_args],"%s",settings->filename_prefix); /* filename prefix */
      copy_args++;
      if (strcmp(settings->compression,"xxxx")!=0){
	strcpy(recorder_args[copy_args],"-c"); copy_args++;
	sprintf(recorder_args[copy_args],"%4s",settings->compression); /* compression settings */
	copy_args++;
      }
      if (strlen(settings->record_start_time)>0){
	strcpy(recorder_args[copy_args],"-s"); copy_args++;
	sprintf(recorder_args[copy_args],"%8sT%8s",settings->record_start_date,settings->record_start_time); /* starting time */
	copy_args++;
      }
      if (settings->vsib_mode!=3){
	strcpy(recorder_args[copy_args],"-m"); copy_args++;
	sprintf(recorder_args[copy_args],"%d",settings->vsib_mode); /* vsib mode */
	copy_args++;
      }
      if (settings->bandwidth!=16){
	strcpy(recorder_args[copy_args],"-w"); copy_args++;
	sprintf(recorder_args[copy_args],"%d",settings->bandwidth); /* bandwidth */
	copy_args++;
      }

      /* launch recorder */
      PrintStatus("launching recorder:");
      sprintf(temp_status,"executing command [%s]",recorder_args[0]);
      PrintStatus(temp_status);
      for (i=1;i<num_args;i++){
	sprintf(temp_status," argument %d: {%s}",i,recorder_args[i]);
	PrintStatus(temp_status);
      }
      
      if ((recorder_pid=fork())==0){ /* are we the child process */
	/* replace the child with the recorder process */
	if ((execv(rec_command,recorder_args))==-1){ /* this is where the code ends for the child process */
	  printf("didn't start recorder!\n");
	  exit(-1);
	}
      }
      /* if we get to here we must be the parent process */
      sprintf(temp_status,"recorder has PID = %d",recorder_pid);
      PrintStatus(temp_status);
      settings->process_id=recorder_pid;

      /* append the PID to the recorder file */
      if ((ret_res=WriteRecorderPID(settings,failure))!=NO_ERROR)
	return(ret_res);

      /* that's it, we're done */
      return(NO_ERROR);
    }
  } else if (command==RECSTOP){
    if (is_recording==0){
      strcpy(failure,NOT_RECORDING_ERR);
      return(NOT_RECORDING);
    } else if (is_recording==1){

      /* to stop the recorder we need to send it the Ctrl-C signal: SIGINT */
      if ((kill(settings->process_id,SIGINT))!=0){ /* something went wrong! */
	strcpy(failure,STOP_FAILED_ERR);
	return(STOP_FAILED);
      }

      /* the recorder has been stopped */
      settings->process_id=-1; /* no more recorder process */
      remove("/tmp/recorder_settings"); /* get rid of the recorder settings file */

      /* change to the parent directory */
      if (settings->in_dir==1){ /* we need to go backwards */
	if ((dir_check=chdir(".."))!=0){
	  strcpy(failure,CHDIR_FAILED_ERR);
	  return(CHDIR_FAILED);
	} else {
	  settings->in_dir=0;
	}
      }

      return(NO_ERROR);
      
    }      
  }
}

/*
  This routine is called when either the client sends a data string,
  or when the defaults are loaded at the start of the server. It looks
  at the data request, and checks to make sure that the requested
  settings are valid, and will return an error if it is not.
*/
int data_handler(char *data,char *failure,recorder *settings){
  char variable[BUFFSIZE],value[BUFFSIZE],temp[BUFFSIZE];
  int returncode=-1;
  int year=-1,month=-1,date=-1,datecheck=-1,hour=-1,minute=-1,i_second=-1,timecheck=-1;
  int compresscheck=-1,temp_time;
  float f_second=-1.0;

  /* does the data request match the format variable=data ? */
  if ((sscanf(data,"%[^=]=%s",variable,value))!=2){ /* no it doesn't */
    returncode=MALFORMED_DATA;
    strcpy(failure,MALFORMED_DATA_ERR);
    return(returncode);
  }

  /* which assignment are we changing? */
  /*   record_time       = the time to record data for */
  /*   record_start_date = the date to start the recording */
  /*   record_start_time = the time to start the recording */
  /*   directory_name    = the directory to put the files in */
  /*   filename_prefix   = the prefix for the output filenames */
  /*   compression       = the channels to record the data for */
  /*   vsib_mode         = the vsib mode for bit length */
  /*   bandwidth         = the recording bandwidth */
  /* some of these options can be set with specific data commands: */
  /*   s2mode            = sets compression */
  /* we can also reset back to defaults using */
  /*   reset=assignment */
  /* or reset everything using */
  /*   reset=all */
  if (strcmp(variable,"record_time")==0){

    /* this setting should be a number, possibly ended by an h or an s */
    if (sscanf(value,"%d%s",&temp_time,temp)!=2){
      returncode=MALFORMED_RECTIME;
      strcpy(failure,MALFORMED_RECTIME_ERR);
      return(returncode);
    } else if ((strcmp(temp,"s")!=0)&&(strcmp(temp,"h")!=0)){ /* the time is not in seconds or hours */
      returncode=MALFORMED_RECTIME;
      strcpy(failure,MALFORMED_RECTIME_ERR);
      return(returncode);
    }

    strcpy(settings->record_time,value);
    returncode=NO_ERROR;

  } else if (strcmp(variable,"record_start_date")==0){

    /* this setting could be yyyy-mm-dd or yyyymmdd */
    /* the value will be stored as yyyymmdd */
    if (strlen(value)==10){
      if ((sscanf(value,"%4d-%2d-%2d",&year,&month,&date))!=3){ /* the date doesn't look like it should */
	returncode=MALFORMED_DATE;
	strcpy(failure,MALFORMED_DATE_ERR);
	return(returncode);
      }
    } else if (strlen(value)==8){
      if ((sscanf(value,"%4d%2d%2d",&year,&month,&date))!=3){ /* the date doesn't look like it should */
	returncode=MALFORMED_DATE;
	strcpy(failure,MALFORMED_DATE_ERR);
	return(returncode);
      }
    } else { /* the length of the string is wrong */
      returncode=MALFORMED_DATE;
      strcpy(failure,MALFORMED_DATE_ERR);
      return(returncode);
    }
    
    /* check the starting date to make sure it's valid and not in the past */
    if ((datecheck=check_date(year,month,date))==MONTH_RANGE){ /* the month is out of range */
      returncode=datecheck;
      strcpy(failure,MONTH_RANGE_ERR);
      return(returncode);
    } else if (datecheck==DATE_RANGE){ /* the date is not valid for the month, or at all */
      returncode=datecheck;
      strcpy(failure,DATE_RANGE_ERR);
      return(returncode);
    } else if (datecheck==IN_THE_PAST){ /* the starting date is in the past */
      returncode=datecheck;
      strcpy(failure,IN_THE_PAST_ERR);
      return(returncode);
    }

    /* finally, store the starting date */
    sprintf(settings->record_start_date,"%4d%02d%02d",year,month,date);
    returncode=NO_ERROR;

  } else if (strcmp(variable,"record_start_time")==0){

    /* this setting could be hh:mm:ss.s or hh:mm:ss or hhmmss.s or hhmmss */
    /* the value will be stored as hhmmss.s */
    fprintf(stdout,"the time length is %d\n",strlen(value));
    if (strlen(value)==10){
      if ((sscanf(value,"%2d:%2d:%4f",&hour,&minute,&f_second))!=3){ /* the time doesn't look like it should */
	returncode=MALFORMED_TIME;
	strcpy(failure,MALFORMED_TIME_ERR);
	return(returncode);
      }
    } else if (strlen(value)==8){
      if ((sscanf(value,"%2d:%2d:%2d",&hour,&minute,&i_second))!=3){ /* could be the other format */
	if ((sscanf(value,"%2d%2d%4f",&hour,&minute,&f_second))!=3){ /* the time doesn't look like it should */
	  returncode=MALFORMED_TIME;
	  strcpy(failure,MALFORMED_TIME_ERR);
	  return(returncode);
	}
      } else { /* need to store the seconds in float format */
	f_second=(float)i_second;
      }
    } else if (strlen(value)==6){
      if ((sscanf(value,"%2d%2d%2d",&hour,&minute,&i_second))!=3){ /* the time doesn't look like it should */
	returncode=MALFORMED_TIME;
	strcpy(failure,MALFORMED_TIME_ERR);
	return(returncode);
      } else { /* need to store the seconds in float format */
	f_second=(float)i_second;
      }
    } else { /* the length of the string is wrong */
      returncode=MALFORMED_TIME;
      strcpy(failure,MALFORMED_TIME_ERR);
      return(returncode);
    }

    /* check the starting time to make sure it's valid and not in the past */
    /* to do this, we need to know the date, so the date needs to be set first, otherwise give an error */
    if (strlen(settings->record_start_date)==0){
      returncode=DATE_NOT_SET;
      strcpy(failure,DATE_NOT_SET_ERR);
      return(returncode);
    }

    sscanf(settings->record_start_date,"%4d%2d%2d",&year,&month,&date);
    if ((timecheck=check_time(year,month,date,hour,minute,f_second))==HOUR_RANGE){ /* the hour is out of range */
      returncode=timecheck;
      strcpy(failure,HOUR_RANGE_ERR);
      return(returncode);
    } else if (timecheck==MINUTE_RANGE){ /* the minute is out of range */
      returncode=timecheck;
      strcpy(failure,MINUTE_RANGE_ERR);
      return(returncode);
    } else if (timecheck==SECOND_RANGE){ /* the second is out of range */
      returncode=timecheck;
      strcpy(failure,SECOND_RANGE_ERR);
      return(returncode);
    } else if (timecheck==IN_THE_PAST){ /* the start time is in the past */
      returncode=timecheck;
      strcpy(failure,IN_THE_PAST_ERR);
      return(returncode);
    }

    /* finally, store the starting time */
    sprintf(settings->record_start_time,"%02d%02d%04.1f",hour,minute,f_second);
    returncode=NO_ERROR;

  } else if (strcmp(variable,"directory_name")==0){
    
    /* the directory name can be anything */
    strcpy(settings->directory_name,value);
    returncode=NO_ERROR;
      
  } else if (strcmp(variable,"filename_prefix")==0){

    /* the file prefix can be anything */
    strcpy(settings->filename_prefix,value);
    returncode=NO_ERROR;

  } else if (strcmp(variable,"compression")==0){

    /* the compression string is four characters long and made from o and x */
    /*    o = discard this channel */
    /*    x = include this channel */

    /* check the length */
    if (strlen(value)!=4){
      returncode=BAD_COMPRESSION;
      strcpy(failure,BAD_COMPRESSION_ERR);
      return(returncode);
    }

    /* check that we're only getting o and x in the string */
    if ((compresscheck=sscanf(value,"%4[ox]",temp))!=1){
      returncode=BAD_COMPRESSION;
      strcpy(failure,BAD_COMPRESSION_ERR);
      return(returncode);
    }

    /* we can store the compression settings */
    strcpy(settings->compression,value);
    returncode=NO_ERROR;

  } else if (strcmp(variable,"s2mode")==0){
    
    /* this data command can be used to quickly set the compression mode */
    /* based on what mode the S2 recorder is in */
    /*   an S2 mode of x = compression ooxx */
    /*   an S2 mode of a = compression oxox */
    if (strcmp(value,"x")==0){
      strcpy(settings->compression,"ooxx");
      returncode=NO_ERROR;
    } else if (strcmp(value,"a")==0){
      strcpy(settings->compression,"oxox");
      returncode=NO_ERROR;
    } else { /* setting not recognised */
      returncode=INVALID_S2_MODE;
      strcpy(failure,INVALID_S2_MODE_ERR);
      return(returncode);
    }
    
  } else if (strcmp(variable,"vsib_mode")==0){

    /* the vsib mode is either 3, for 8-bit data (default), */
    /* or 2, for 16-bit data */
    if ((atoi(value)!=2)&&(atoi(value)!=3)){ /* not a valid vsib mode */
      returncode=BAD_VSIB_MODE;
      strcpy(failure,BAD_VSIB_MODE_ERR);
      return(returncode);
    }

    /* otherwise, we store the vsib mode setting */
    settings->vsib_mode=atoi(value);
    returncode=NO_ERROR;

  } else if (strcmp(variable,"bandwidth")==0){
    
    /* the bandwidth in MHz must be a power of 2 */
    /* ie. 1, 2, 4, 8, 16 */
    if ((atoi(value)!=1)&&(atoi(value)!=2)&&(atoi(value)!=4)
	&&(atoi(value)!=8)&&(atoi(value)!=16)){ /* not a valid bandwidth */
      returncode=BAD_BANDWIDTH;
      strcpy(failure,BAD_BANDWIDTH_ERR);
      return(returncode);
    }

    /* otherwise, we store the bandwidth setting */
    settings->bandwidth=atoi(value);
    returncode=NO_ERROR;
  
  } else if (strcmp(variable,"reset")==0){

    returncode=NO_ERROR;
    if (strcmp(value,"record_time")==0){
      if ((LoadSettings(settings,RESET_RECORD_TIME,failure))!=NO_ERROR){
	returncode=RESET_FAILED;
	strcpy(failure,RESET_FAILED_ERR);
      }
    } else if (strcmp(value,"record_start_date")==0){
      if ((LoadSettings(settings,RESET_RECORD_START_DATE,failure))!=NO_ERROR){
	returncode=RESET_FAILED;
	strcpy(failure,RESET_FAILED_ERR);
      }
    } else if (strcmp(value,"record_start_time")==0){
      if ((LoadSettings(settings,RESET_RECORD_START_TIME,failure))!=NO_ERROR){
	returncode=RESET_FAILED;
	strcpy(failure,RESET_FAILED_ERR);
      }
    } else if (strcmp(value,"directory_name")==0){
      if ((LoadSettings(settings,RESET_DIRECTORY_NAME,failure))!=NO_ERROR){
	returncode=RESET_FAILED;
	strcpy(failure,RESET_FAILED_ERR);
      }
    } else if (strcmp(value,"filename_prefix")==0){
      if ((LoadSettings(settings,RESET_FILENAME_PREFIX,failure))!=NO_ERROR){
	returncode=RESET_FAILED;
	strcpy(failure,RESET_FAILED_ERR);
      }
    } else if (strcmp(value,"compression")==0){
      if ((LoadSettings(settings,RESET_COMPRESSION,failure))!=NO_ERROR){
	returncode=RESET_FAILED;
	strcpy(failure,RESET_FAILED_ERR);
      }
    } else if (strcmp(value,"vsib_mode")==0){
      if ((LoadSettings(settings,RESET_VSIB_MODE,failure))!=NO_ERROR){
	returncode=RESET_FAILED;
	strcpy(failure,RESET_FAILED_ERR);
      }
    } else if (strcmp(value,"bandwidth")==0){
      if ((LoadSettings(settings,RESET_BANDWIDTH,failure))!=NO_ERROR){
	returncode=RESET_FAILED;
	strcpy(failure,RESET_FAILED_ERR);
      }
    } else if (strcmp(value,"all")==0){
      if ((LoadSettings(settings,RESET_ALL,failure))!=NO_ERROR){
	returncode=RESET_FAILED;
	strcpy(failure,RESET_FAILED_ERR);
      }
    } else { /* the value is not a recognised data name */
      returncode=INVALID_RESET;
      strcpy(failure,INVALID_RESET_ERR);
    }
    return(returncode);

  } else {
    
    /* this means that the requested data doesn't exist */
    returncode=BAD_DATA;
    strcpy(failure,BAD_DATA_ERR);
    return(returncode);

  }
    

  /* if we've made it this far, returncode will be NO_ERROR */
  return(returncode);

}

/*
  This routine checks that the time passed to it is an actual time, and not in the past.
  It assumes that the date portion of the time has already been checked, and is valid and
  not in the past.
*/
int check_time(int year,int month,int date,int hour,int minute,float second){
  time_t time_now;
  struct tm *time_ptr;
  int gmt_year,gmt_month,gmt_day,gmt_hour,gmt_min,gmt_sec;
  float dwhole,gmt_dwhole,dfrac,gmt_dfrac,start_time,gmt_time;

  /* get the current time */
  time(&time_now);
  time_ptr=gmtime(&time_now);
  gmt_year=(time_ptr->tm_year)+1900;
  gmt_month=(time_ptr->tm_mon)+1;
  gmt_day=(time_ptr->tm_mday);
  gmt_hour=time_ptr->tm_hour;
  gmt_min=time_ptr->tm_min;
  gmt_sec=time_ptr->tm_sec;
  
  /* get the number of days since J2000.0 */
  /* for the starting date                          for the current GMT */
  dwhole=367*year;                                gmt_dwhole=367*gmt_year;
  dwhole-=(int)(7*(year+(int)((month+9)/12))/4);  gmt_dwhole-=(int)(7*(gmt_year+(int)((gmt_month+9)/12))/4);
  dwhole+=(int)(275*month/9);                     gmt_dwhole+=(int)(275*month/9);
  dwhole+=date-730531.5;                          gmt_dwhole+=gmt_day-730531.5;
  dfrac=(float)hour+(float)minute/60;             gmt_dfrac=(float)gmt_hour+(float)gmt_min/60;
  dfrac+=second/3600.0;                           gmt_dfrac+=(float)gmt_sec/3600;
  dfrac/=24;                                      gmt_dfrac/=24;
  start_time=dwhole+dfrac;                        gmt_time=gmt_dwhole+gmt_dfrac;

  /* check for valid hour */
  if ((hour<0)||(hour>23))
    return(HOUR_RANGE);

  /* check for valid minute */
  if ((minute<0)||(minute>59))
    return(MINUTE_RANGE);

  /* check for valid second */
  if ((second<0.0)||(second>59.9))
    return(SECOND_RANGE);

  /* check that the time isn't in the past */
  if (start_time<gmt_time)
    return(IN_THE_PAST);

  /* everything checks out */
  return(NO_ERROR);
}
		 
/*
  This routine checks that the date passed to it is an actual date, and not in the past
*/
int check_date(int year,int month,int date){
  time_t time_now;
  struct tm *time_ptr;
  int gmt_year,gmt_month,gmt_day;

  /* get the current date */
  time(&time_now);
  time_ptr=gmtime(&time_now);
  gmt_year=(time_ptr->tm_year)+1900;
  gmt_month=(time_ptr->tm_mon)+1;
  gmt_day=(time_ptr->tm_mday);

  /* check for valid month */
  if ((month<1)||(month>12))
    return(MONTH_RANGE);

  /* check for valid day-of-month */
  if ((date<1)||(date>31))
    return(DATE_RANGE);

  switch (month){
  case 4: case 6: case 9: case 11:
    if (date>30)
      return(DATE_RANGE);
    break;
  case 2:
    /* determine if it's a leap year */
    if (xmody(year,4)!=0){
      if (date>28)
	return (DATE_RANGE);
    } else if (xmody(year,400)==0){
      if (date>29)
	return (DATE_RANGE);
    } else if (xmody(year,100)==0){
      if (date>28)
	return (DATE_RANGE);
    } else {
      if (date>28)
	return (DATE_RANGE);
    }
    break;
  }

  /* check that the date isn't in the past */
  if (date<gmt_day){
    /* then the month needs to be bigger */
    if (month<=gmt_month){
      /* then the year needs to be bigger */
      if (year<=gmt_year){
	/* in the past */
	return(IN_THE_PAST);
      }
    }
  } else {
    /* then the month needs to be the same or bigger */
    if (month<gmt_month){
      /* then the year needs to be bigger */
      if (year<=gmt_year){
	/* in the past */
	return(IN_THE_PAST);
      }
    } else {
      /* then the year can't be smaller */
      if (year<gmt_year){
	/* in the past */
	return(IN_THE_PAST);
      }
    }
  }
  return(NO_ERROR);
}

/*
  This routine takes the modulo of x/y
*/
int xmody(int x,int y){
  while(x>=y)
    x-=y;
  return(x);
}

/*
  Send the confirmation message back to the client, or the error
  code and error message if the request has failed.
*/
void send_confirmation(int errorcode,char *errormessage,int sock){
  char tag[BUFFSIZE];
  int taglen=0;

  if (errorcode==NO_ERROR){ /* successful completion of request */
    strcpy(tag,"<succ />");
    taglen=strlen(tag);
    PrintStatus("Request successful.");
  } else { /* something went wrong */
    sprintf(tag,"<fail>error %d: %s</fail>",errorcode,errormessage);
    taglen=strlen(tag);
    PrintStatus(tag);
  }
  if (send(sock, tag, taglen+1, 0) != taglen+1)
    Die("Failed to send confirmation to client",sock);

}

/*
  This routine is called if a message is sent to the server.
  The message is printed to the screen, unless it is a zero
  length message, which causes an error.
*/
int message_handler(char *message,char *failure){
  int returncode=-1;

  failure[0]='\0';
  if (strlen(message)<1){
    returncode=SHORT_MESG;
    strcpy(failure,SHORT_MESG_ERR);
    return(returncode);
  }
   
  fprintf(stdout,"Message received from client:\n");
  fprintf(stdout,"%s\n",message);
  fflush(stdout);
  returncode=NO_ERROR;

  return(returncode);
}

/*
  The main routine creates the socket, binds to it for listening,
  and waits until a connection is made, before calling the
  HandleClient routine.
*/
int main(int argc, char *argv[]) {
  int serversock, clientsock;
  struct sockaddr_in mesgserver, mesgclient;
  char failmsg[BUFFSIZE];
  recorder settings;

  /* initialise the recorder settings */
/*   if ((LoadSettings(&settings,RESET_INITIAL,failmsg))!=NO_ERROR) */
/*     Die(failmsg,serversock); /\* die if an error occurs on startup *\/ */
  
  /* check for correct usage */
/*   if (argc != 2) { */
/*     fprintf(stderr, "USAGE: %s <port>\n",argv[0]); */
/*     exit(1); */
/*   } */

  /* Create the TCP socket */
  close(serversock);
  if ((serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    Die("Failed to create socket",serversock);

  /* Construct the server sockaddr_in structure */
  memset(&mesgserver, 0, sizeof(mesgserver));       /* Clear struct */
  mesgserver.sin_family = AF_INET;                  /* Internet/IP */
  mesgserver.sin_addr.s_addr = htonl(INADDR_ANY);   /* Incoming addr */
  mesgserver.sin_port = htons(LISTENPORT);          /* server port */
  
  /* Bind the server socket */
  if (bind(serversock, (struct sockaddr *) &mesgserver,
	   sizeof(mesgserver)) < 0)
    Die("Failed to bind the server socket",serversock);

  /* Listen on the server socket */
  if (listen(serversock, MAXPENDING) < 0)
    Die("Failed to listen on server socket",serversock);
           
  /* Run until cancelled */
  while (1) {
    unsigned int clientlen = sizeof(mesgclient);
    /* Wait for client connection */
    if ((clientsock =
	 accept(serversock, (struct sockaddr *) &mesgclient,
		&clientlen)) < 0)
      Die("Failed to accept client connection",serversock);

    fprintf(stdout, "Client connected: %s\n",
	    inet_ntoa(mesgclient.sin_addr));
    HandleClient(clientsock,&settings);
  }
}

/*
  This routine is called at the start of main to set the defaults for
  the parameters in the recorder structure. It is also used to detect
  whether there are user defined defaults (in /etc/recorder_server.conf).
  If there has been a server crash while the recorder server was running,
  the file /tmp/recorder_settings will exist, and this routine will
  take over control of the recording, using the settings from this file.
*/
int LoadSettings(recorder *settings,int RESET,char *failmsg){
  FILE *defaults=NULL;
  char in_line[BUFFSIZE],temp[BUFFSIZE];
  char variable[BUFFSIZE],value[BUFFSIZE],data[BUFFSIZE],response[BUFFSIZE];
  int i_response=-1,line=-1,lerr=-1;

  /* defaults */
  PrintStatus("Setting defaults...");
  if (RESET_RECORD_TIME & RESET)
    strcpy(settings->record_time,"12h");    /* default of 12 hours; this parameter is required */
  if (RESET_RECORD_START_DATE & RESET)
    settings->record_start_date[0] = '\0';  /* no default, but not necessarily needed */
  if (RESET_RECORD_START_TIME & RESET)
    settings->record_start_time[0] = '\0';  /* no default, but not necessarily needed */
  if (RESET_DIRECTORY_NAME & RESET)
    settings->directory_name[0]    = '\0';  /* no default, and is required */
  if (RESET_FILENAME_PREFIX & RESET)
    settings->filename_prefix[0]   = '\0';  /* no default, and is required */
  if (RESET_COMPRESSION & RESET)
    strcpy(settings->compression,"xxxx");   /* all channels enabled by default */
  if (RESET_VSIB_MODE & RESET)
    settings->vsib_mode            = 3;     /* 8-bit recording default */
  if (RESET_BANDWIDTH & RESET)
    settings->bandwidth            = 16;    /* 16 MHz bandwidth default */
  if (RESET==RESET_INITIAL){
    settings->process_id           = -1;    /* this only gets set if a recorder process is running */
    strcpy(settings->status_checker,        /* this string will contain the status of the checker */
	   "Checker stopped.");
    settings->in_dir               = 0;     /* if we've just started, we're not in the output directory */
  }

  /* are there user/system defaults? */
  if ((defaults=fopen("/etc/recorder_server.conf","r"))!=NULL){ /* file exists */
    PrintStatus("Reading system defaults...");
    line=0;
    while((fgets(in_line,BUFFSIZE,defaults))!=NULL){
      line++;
      /* the lines of this file should look like */
      /* variable   value */
      /* only the data variables are allowed to be specified */
      /* comment lines are allowed by placing a # symbol at the start of the line */
      if ((sscanf(in_line,"%s %s",variable,value))==2){ /* format is correct */
	if (in_line[0]!='#'){ /* not a comment line */
	  /* check whether we've been asked to set this variable */
	  if (((strcmp(variable,"record_time")==0)&&(RESET_RECORD_TIME & RESET))||
	      ((strcmp(variable,"record_start_date")==0)&&(RESET_RECORD_START_DATE & RESET))||
	      ((strcmp(variable,"record_start_time")==0)&&(RESET_RECORD_START_TIME & RESET))||
	      ((strcmp(variable,"directory_name")==0)&&(RESET_DIRECTORY_NAME & RESET))||
	      ((strcmp(variable,"filename_prefix")==0)&&(RESET_FILENAME_PREFIX & RESET))||
	      ((strcmp(variable,"compression")==0)&&(RESET_COMPRESSION & RESET))||
	      ((strcmp(variable,"vsib_mode")==0)&&(RESET_VSIB_MODE & RESET))||
	      ((strcmp(variable,"bandwidth")==0)&&(RESET_BANDWIDTH & RESET))){
	    /* we'll use the data_handler routine to check the values are legal */
	    sprintf(data,"%s=%s",variable,value);
	    if ((i_response=data_handler(data,response,settings))!=NO_ERROR){
	      sprintf(temp,"error on line %d of recorder_server.conf!",line);
	      PrintStatus(temp);
	      PrintStatus(in_line);
	      sprintf(temp,"error %d: %s",i_response,response);
	      PrintStatus(temp);
	    }
	  }
	}
      } else if (in_line[0]!='#'){ /* not a comment line */
	sprintf(temp,"error on line %d of recorder_server.conf!",line);
	PrintStatus(temp);
	sprintf(temp,"%s is incorrectly specified",in_line);
	PrintStatus(temp);
      }
    }
    fclose(defaults);
  }

  /* was there a server crash while recording? */
  if ((defaults=fopen("/tmp/recorder_settings","r"))!=NULL){ /* must be currently recording */
    fclose(defaults);
    if ((lerr=LoadRecorderSettings(settings,failmsg))!=NO_ERROR)
      return(lerr);
  }

  /* update the recorder settings now */
  if ((lerr=UpdateRecorderStatus(settings,failmsg))!=NO_ERROR)
    return(lerr);

  return(NO_ERROR);

}

/*
  This routine is used to load the recorder settings if the server crashed
  while the recorder was running. This is so we can recover gracefully without
  needing to restart the recording process, or having to resort to manual
  control.
*/
int LoadRecorderSettings(recorder *settings,char *failmsg){
  FILE *recorder_log=NULL;
  char settings_line[BUFFSIZE],paramname[BUFFSIZE],paramcval[BUFFSIZE],passdata[BUFFSIZE],failure[BUFFSIZE];
  int i,numparams,paramival=-1,j,retval;
  float paramfval=-1.0;

  if ((recorder_log=fopen("/tmp/recorder_settings","r"))==NULL){ /* the file isn't there */
    strcpy(failmsg,FILE_UNREADABLE_ERR);
    return(FILE_UNREADABLE);
  }

  /* load the recorder settings into the recorder structure */
  numparams=8; /* there are 8 parameters to read in, not including the recorder PID */
  for (i=0;i<numparams;i++){
/*     printf("i = %d\n",i); */
    for (j=0;j<BUFFSIZE;j++){
      settings_line[j]='\0';
    }
    if ((fgets(settings_line,BUFFSIZE,recorder_log))==NULL){ /* shouldn't get an EOF error! */
      strcpy(failmsg,UNEXPECTED_EOF_ERR);
      return(UNEXPECTED_EOF);
    }
    switch (i){
    case 0: case 1: case 2: case 3: case 4: case 5:
      sscanf(settings_line,"%s %s\n",paramname,paramcval);
      sprintf(passdata,"%s=%s",paramname,paramcval);
      break;
    case 6: case 7:
      sscanf(settings_line,"%s %d\n",paramname,&paramival);
      sprintf(passdata,"%s=%d",paramname,paramival);
      break;
    }
    if ((i!=1)&&(i!=2)){
      if ((data_handler(passdata,failure,settings))!=NO_ERROR){
	strcpy(failmsg,CORRUPT_SETTINGS_ERR);
	return(CORRUPT_SETTINGS);
      }
    } else { /* we don't want to check the start time and date - it is possible the recorder has already started */
      if (i==1){
	if (strcmp(paramcval,"-1")==0){
	  settings->record_start_date[0]='\0';
	} else {
	  strcpy(settings->record_start_date,paramcval);
	}
      } else {
	if (strcmp(paramcval,"-1")==0){
	  settings->record_start_time[0]='\0';
	} else {
	  strcpy(settings->record_start_time,paramcval);
	}
      }
    }
  }
  /* now get the recorder PID */
  if ((fgets(settings_line,BUFFSIZE,recorder_log))==NULL){ /* shouldn't get an EOF error! */
    strcpy(failmsg,UNEXPECTED_EOF_ERR);
    return(UNEXPECTED_EOF);
  }
  sscanf(settings_line,"%s %d",paramname,&paramival);
  /* check if the recorder is still running */
  if (checkrunning(paramival)==1){ /* yep still running */
    settings->process_id=paramival;
    /* try to change to the recorder directory */
    if (settings->in_dir==0){ /* need to change directory */
      if ((chdir(settings->directory_name))!=0){ /* can't change - probably started up in the wrong directory */
	strcpy(failmsg,CHDIR_FAILED_ERR);
	return(CHDIR_FAILED);
      } else {
	settings->in_dir=1;
      }
    }
  } else { /* nope it's stopped/finished */
    settings->process_id=-1;
  }

  /* load successful */
  if ((retval=UpdateRecorderStatus(settings,failmsg))!=NO_ERROR)
    return(retval);
  return(NO_ERROR);

}

/*
  This routine is called by the recorder_control routine just before it issues
  the recorder start command. It is supposed to write out all the data in the
  recorder structure except the recorder PID, which is unknown at the time it
  is called, and will be written by another routine.
*/
int WriteRecorderSettings(recorder *settings,char *failure){
  FILE *recorder_log=NULL;

  /* we don't bother checking if the file exists, only if we can open it for writing */
  if ((recorder_log=fopen("/tmp/recorder_settings","w"))==NULL){
    strcpy(failure,FILE_UNWRITABLE_ERR);
    return(FILE_UNWRITABLE);
  }

  fprintf(recorder_log,"%-17s   %s\n","record_time",settings->record_time);
  if (strlen(settings->record_start_date)!=0){
    fprintf(recorder_log,"%-17s   %s\n","record_start_date",settings->record_start_date);
    fprintf(recorder_log,"%-17s   %s\n","record_start_time",settings->record_start_time);
  } else {
    fprintf(recorder_log,"%-17s   %d\n","record_start_date",-1);
    fprintf(recorder_log,"%-17s   %d\n","record_start_time",-1);
  }
  fprintf(recorder_log,"%-17s   %s\n","directory_name",settings->directory_name);
  fprintf(recorder_log,"%-17s   %s\n","filename_prefix",settings->filename_prefix);
  fprintf(recorder_log,"%-17s   %s\n","compression",settings->compression);
  fprintf(recorder_log,"%-17s   %d\n","vsib_mode",settings->vsib_mode);
  fprintf(recorder_log,"%-17s   %d\n","bandwidth",settings->bandwidth);

  fclose(recorder_log);
  return(NO_ERROR);

}

/*
  This routine is called by the recorder_control routine after the
  recorder has been started. It will open the recorder settings file
  for writing at the end of the file, and append the PID of the
  recorder process.
*/
int WriteRecorderPID(recorder *settings,char *failure){
  FILE *recorder_log=NULL;

  /* check the file exists */
  if ((recorder_log=fopen("/tmp/recorder_settings","r"))==NULL){ /* not found - this should be impossible */
    strcpy(failure,SETTINGS_GONE_ERR);
    return(SETTINGS_GONE);
  }

  fclose(recorder_log);
  /* open the file for appended writing */
  if ((recorder_log=fopen("/tmp/recorder_settings","a"))==NULL){ /* can't write to it */
    strcpy(failure,FILE_UNWRITABLE_ERR);
    return(FILE_UNWRITABLE);
  }

  fprintf(recorder_log,"%-17s   %d\n","process_id",settings->process_id);

  fclose(recorder_log);
  return(NO_ERROR);

}

/*
  This routine is called whenever a request for the status of the recorder
  is made. It returns a message saying whether the recorder is running, and
  how long it can run for given the remaining space on the drive.
*/
int UpdateRecorderStatus(recorder *settings,char *failure){
  char dfcommand[BUFFSIZE],dfbuf[BUFFSIZE],dfstring[BUFFSIZE],temps[BUFFSIZE],timefree[BUFFSIZE];
  char firstpart[BUFFSIZE],temp1[BUFFSIZE],temp2[BUFFSIZE];
  FILE *dfoutput=NULL;
  int i,tempi,datarate,nchans,nhours,ittime,status,chldstatus;
  long freespace,templ;
  float freetime,outtime;

  /* First we need to determine the space remaining on the drive */
  /* we'll use a dirty hack for this! */
  strcpy(dfcommand,"df . > /tmp/df_result"); /* I feel dirty already */
  system(dfcommand);
  if ((dfoutput=fopen("/tmp/df_result","r"))==NULL){
    strcpy(failure,FREESPACE_ERR);
    return(FREESPACE);
  }
  while ((fgets(dfbuf,BUFFSIZE,dfoutput))!=NULL)
    strcpy(dfstring,dfbuf);
  fclose(dfoutput);

  /* scan the string read last, it should contain the free space */
  if (sscanf(dfstring,"%s %ld %ld %ld %d%% %s",temps,&templ,&templ,&freespace,&tempi,temps)!=6){
    /* the df string doesn't look like we thought it should */
    strcpy(failure,FREESPACE_ERR);
    return(FREESPACE);
  }
  /* remove the hack file, maybe that will make me feel clean */
  remove("/tmp/df_result"); /* nope still dirty... */

  /* now we figure out what the data rate of the observations is */
  nchans=0;
  for (i=0;i<4;i++){ /* figure out the number of channels we're recording */
    if (settings->compression[i]='x')
      nchans++;
  }
  fprintf(stdout,"nchans = %d\n",nchans);
  if (settings->bandwidth<=16){ /* normal bandwidth, less than the sampling rate, so 8 bits per sample */
    datarate = nchans            /* number of channels recorded */
      * (settings->bandwidth)*2  /* the Nyquist sampling frequency */
      * 2;                       /* 2 bit digitisation */
    fprintf(stdout,"normal bandwidth = %d MHz, datarate = %d Mbps\n",settings->bandwidth,datarate);
  } else { /* large bandwidth, more than the sampling rate, so 16 bits per sample */
    datarate = nchans            /* number of channels recorded */
      * 32                       /* the sampling frequency */
      * 2                        /* 2 bit digitisation */
      * 2;                       /* 2 bytes per sample */
  }
  /* the datarate is in Mbps, and the freespace is in KB */
  freetime = (float)(freespace/1024)  /* free space in MB */
    / (float)(datarate/8);            /* data rate in MBps */
  fprintf(stdout,"freetime = %f s\n",freetime);
  /* freetime is in seconds, determine what is the best unit to express it in */
  if (freetime<(10*60)){ /* less than 10 minutes remaining, we'll express the time in seconds */
    ittime=(int)freetime; /* we've rounded the time down to the nearest second */
    sprintf(timefree,"%ds",ittime);
  } else if (freetime<(3*60*60)){ /* less than 3 hours remaining, we'll express the time in minutes */
    outtime=(freetime/60.0);
    ittime=(int)outtime; /* we've rounded the time down to the nearest minute */
    sprintf(timefree,"%dm",ittime);
  } else { /* more than 3 hours remaining, we'll express the time in hours and minutes */
    nhours=0;
    outtime=freetime/60.0;
    ittime=(int)outtime;
    for (i=ittime;i>=60;i-=60)
      nhours++;
    sprintf(timefree,"%dh%dm",nhours,i);
  }

  /* now generate the recorder status */
  if (settings->process_id!=-1){ /* we're recording at the moment */
    /* we could be stopped, and our recorder child zombified */
    if ((chldstatus=waitpid(settings->process_id,&status,WNOHANG))==0){ /* recorder still running */
      sprintf(temp1,"Recorder running.\nvsib_record -t %s -o %s",settings->record_time,settings->filename_prefix);
      if (strcmp(settings->compression,"xxxx")!=0){
	sprintf(temp2,"%s -c %s",temp1,settings->compression);
	strcpy(temp1,temp2);
      }
      if (strlen(settings->record_start_time)>0){
	sprintf(temp2,"%s -s %8sT%8s",temp1,settings->record_start_date,settings->record_start_time);
	strcpy(temp1,temp2);
      }
      if (settings->vsib_mode!=3){
	sprintf(temp2,"%s -m %d",temp1,settings->vsib_mode);
	strcpy(temp1,temp2);
      }
      if (settings->bandwidth!=16){
	sprintf(temp2,"%s -w %d",temp1,settings->bandwidth);
	strcpy(temp1,temp2);
      }
      strcpy(firstpart,temp1);
    } else if (chldstatus==settings->process_id){ /* recorder has stopped */
      strcpy(firstpart,"Recorder stopped.");
    }
  } else {
    strcpy(firstpart,"Recorder stopped.");
  }
  sprintf(settings->status_recorder,"%s\nAvailable recording space (time): %d MB (%s)",firstpart,(freespace/1024),timefree);
  return(NO_ERROR);
}

/*
  This routine is called when the checker child process finishes.
  It needs to update the checker status and kill the child.
*/
void child_handler(int sig){
  FILE *checkoutput=NULL;
  char checkbuf[BUFFSIZE];

  /* our first job is to kill the child */
  kill(child_pid,SIGINT);

  /* try to open the file with the output from the checker */
  if ((checkoutput=fopen("/tmp/checker_output","r"))==NULL){
    /* oops, all that check time and we get an error! */
    /* we can't return the error either because we can't communicate */
    /* so we return the error in the checker status buffer */
    strcpy(recorder_pointer->status_checker,"Checking failed.");
  } else {
    /* we're going to copy the output from the file straight into the checker status buffer */
    recorder_pointer->status_checker[0]='\0'; /* clear the status buffer */
    while((fgets(checkbuf,BUFFSIZE,checkoutput))!=NULL)
      sprintf(recorder_pointer->status_checker,"%s%s",recorder_pointer->status_checker,checkbuf); /* we're not stripping the newlines */
    fclose(checkoutput);
  }
}

/*
  This routine is called to see if the recorder is still running, by
  checking for the PID number that we were given when it started.
*/
int checkrunning(int pid){
  struct stat check_dir;
  int dir_check=-1;
  char buf[BUFFSIZE];

  /* check for the existence of the directory PID in the proc fs */
  sprintf(buf,"/proc/%d",pid); /* the name of the directory */
  if ((dir_check=stat(buf,&check_dir))==0){ /* something with the directory name already exists */
    if (S_ISDIR(check_dir.st_mode)){
      return(1); /* the process is still running */
    } else {
      return(0); /* the process is no longer running */
    }
  }
}
